﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using USNetFramework.PacketBuilder;
using USNetFramework.RPC;
using USNetFramework.Utils;

namespace USNetFramework.Network
{
    public enum SocketEvent
    {
        Unknow          = 0,
        Connected       = 1,
        Disconnected    = 2,
        TimeOut         = 3,
    }

    /// <summary>
    ///     封装异步tcp socket,提供发送接收缓存以及消息包分发等
    /// </summary>
    public class NetSocket
    {
        // 静态属性
        private static uint s_uiId = 0;


        // 套接字属性
        private Socket m_sSocket = null;
        private List<ISocketListener> m_lListeners = null;
        private uint m_uiId { get; } = 0xFFFFFFFF;

        private byte[] m_bufferRecv = null;
        private int m_uiRecvBufferSize = 0;
        private uint m_uiRecvPacketCount = 0;
        private List<NetPacket> m_lNetPacketRecv = null;
        private List<NetPacket> m_lNetPacketSend = null;


        // 心跳检测
        private uint m_uiLastHeartBeatTime = 0;

        // 派发线程
        private Thread m_subThread;

        // 连接属性
        private string m_strConnetHost = null;
        private uint m_uiConnetPort = 0xFFFFFFFF;

        // 网络包构造器
        private IPacketBuilder m_objNetPacketBuilder = null;

        // 锁
        private object m_lockObj = new object();


        public NetSocket(IPacketBuilder builder)
        {
            m_uiId = s_uiId++;

            m_bufferRecv = new byte[NetOption.SOCKET_TCP_BUFFER];
            m_lNetPacketRecv = new List<NetPacket>();
            m_lNetPacketSend = new List<NetPacket>();

            m_lListeners = new List<ISocketListener>();

            m_objNetPacketBuilder = builder;

            //mark: 现在unity不能多线程socket调用，暂时取消
            //m_subThread = new Thread(SubMain);
            //m_subThread.Start();
        }

        public void Regiester(ISocketListener o)
        {
            m_lListeners.Add(o);
        }

        public void UnRegiester(ISocketListener o)
        {
            m_lListeners.Remove(o);
        }

        public void SetRpcEnable(bool _enbale)
        {
            NetOption.RPCEnable = _enbale;
        }

        /// <summary>
        ///     创建一个连接到域名或者IP的连接
        /// </summary>
        /// <param name="strUrl">连接地址</param>
        /// <param name="uiPort">连接端口</param>
        /// <returns>是否连接成功</returns>
        public virtual bool Connect(string strUrl, uint uiPort)
        {
            Log.WriteInfo("Connect:" + strUrl + " port:" + uiPort);
            if (null != m_sSocket)
            {
                Close();
            }

            m_strConnetHost = strUrl;
            if (strUrl.Contains("http://") || strUrl.Contains("https://"))
            {
                m_strConnetHost = new Uri(strUrl).Host;
            }
            m_uiConnetPort = uiPort;

            m_uiRecvBufferSize = 0;
            m_uiRecvPacketCount = 0;

            if (Array.Exists(NetOption.DomainSuffixList, s => { return m_strConnetHost.Contains(@s); }))
            {
                Dns.BeginGetHostAddresses(m_strConnetHost, new AsyncCallback(GetHostCallback), null);
            }
            else
            {
                AsyncConnect(IPAddress.Parse(m_strConnetHost), m_uiConnetPort);
            }

            return true;
        }

        /// <summary>
        ///     创建一个连接到指定IP指定端口的连接
        /// </summary>
        /// <param name="host">连接IP</param>
        /// <param name="port">连接端口</param>
        private void AsyncConnect(IPAddress ipAddr, uint uiPort)
        {
            try
            {
                Log.WriteInfo("AsyncConnect:" + ipAddr + " port:" + uiPort);

                lock(m_lockObj)
                {
                    if (null == m_sSocket)
                    {
                        //m_sSocket = new Socket(AddressFamily.InterNetwork,
                        //SocketType.Stream,
                        //System.Net.Sockets.ProtocolType.Tcp);
                        m_sSocket = new Socket(ipAddr.AddressFamily,
                            SocketType.Stream,
                            System.Net.Sockets.ProtocolType.Tcp);
                        m_sSocket.SetSocketOption(SocketOptionLevel.Socket,
                            SocketOptionName.Linger,
                            new LingerOption(false, 0));
                        m_sSocket.SetSocketOption(SocketOptionLevel.Socket,
                            SocketOptionName.ReceiveBuffer,
                            NetOption.SOCKET_MAX_RECV_SIZE);
                        m_sSocket.SetSocketOption(SocketOptionLevel.Socket,
                            SocketOptionName.ReuseAddress,
                            true);

                        m_sSocket.Blocking = true;
                        m_sSocket.NoDelay = true;
                        m_sSocket.ReceiveTimeout = NetOption.Timeout;
                        m_sSocket.SendTimeout = NetOption.Timeout;
                    }

                    m_sSocket.BeginConnect(
                        new IPEndPoint(ipAddr, (int)uiPort),
                        new AsyncCallback(OnConnected),
                        m_sSocket);
                }
            }
            catch (SocketException sex)
            {
                OnHandleSocketException(sex);
                throw sex;
            }
        }


        private void GetHostCallback(IAsyncResult asyncGetHost)
        {
            try
            {
                IPAddress[] addresses = Dns.EndGetHostAddresses(asyncGetHost);
                Log.WriteInfo("GetHostCallback.  len:" +
                    addresses.Length + " ip:" +
                    (addresses.Length > 0 ? addresses[0].ToString() : ""));
                if (addresses.Length > 0)
                {
                    AsyncConnect(addresses[0], m_uiConnetPort);
                }
            }
            catch (SocketException sex)
            {
                OnHandleSocketException(sex);
                throw sex;
            }
        }


        private void OnConnected(IAsyncResult asyncConnect)
        {
            Log.WriteInfo("AsyncConnect Connect Callback");
            Socket socket = (Socket)asyncConnect.AsyncState;
            if (null == m_sSocket)
            {
                Log.WriteError("Connect Callback Socket is null");
                return;
            }

            try
            {
                socket.EndConnect(asyncConnect);
                if (asyncConnect.IsCompleted && socket.Connected)
                {
                    OnHandleSocketEvent(SocketEvent.Connected);
                    Receive(socket);
                }
            }
            catch(SocketException sex)
            {
                OnHandleSocketException(sex);
            }
        }

        private void Receive(Socket socket)
        {
            if (null == socket || !socket.Connected)
                return;

            try
            {   
                socket.BeginReceive(
                    m_bufferRecv,
                    m_uiRecvBufferSize,
                    NetOption.SOCKET_MAX_RECV_SIZE,
                    SocketFlags.None,
                    new AsyncCallback(OnReceived),
                    m_sSocket
                );
            }
            catch(SocketException sex)
            {
                OnHandleSocketException(sex);
                throw sex;
            }
        }

        private void OnReceived(IAsyncResult asyncRecv)
        {
            if (null == m_sSocket || !m_sSocket.Connected)
                return;

            try
            {
                Socket socket = (Socket)asyncRecv.AsyncState;
                if(!asyncRecv.IsCompleted)
                    return;

                int length = socket.EndReceive(asyncRecv);
                if(length > 0)
                {
                    m_uiRecvBufferSize += length;
                    
                    // 分包
                    while(true)
                    {
                        NetPacket packet = m_objNetPacketBuilder.Decode(m_bufferRecv, m_uiRecvBufferSize);
                        if (packet == null)
                        {
                            break;
                        }
                        else
                        {
                            int dataTotalSiz = (int)packet.NetSize + NetPacket.NetPacketLenSize;                    // 2字节的长度信息
                            m_uiRecvBufferSize = m_uiRecvBufferSize - dataTotalSiz;
                            Buffer.BlockCopy(m_bufferRecv, dataTotalSiz, m_bufferRecv, 0, m_uiRecvBufferSize);
                            m_uiRecvPacketCount++;

                            lock(((ICollection)m_lNetPacketRecv).SyncRoot)
                            {
                                m_lNetPacketRecv.Add(packet);
                            }                         
                        }
                    }
                }

                // 分包后依然等于或大于最大buff长度
                if (m_uiRecvBufferSize >= NetOption.SOCKET_TCP_BUFFER)
                {
                    Disconnect();
                }
                else
                {
                    Receive(socket);
                }
            }
            catch (ObjectDisposedException odex)
            {
                Log.WriteError(odex.ToString());
            }
            catch (SocketException sex)
            {
                OnHandleSocketException(sex);
            }
        }

        public void OnUpdate()
        {
            if(NetOption.HeartBeatEnable)
            {
                DoSendHeartBeat();
            }

            DoSend();
            DoDispatch();
        }


        // 子线程
        public void SubMain()
        {
            while(true)
            {
                Thread.Sleep(1);
                OnUpdate();
            }
        }

        //public virtual bool SendRpc(NetPacket packet, RpcEvent callback)
        //{
        //    if (!NetOption.RPCEnable)
        //        return false;

        //    if (!RpcManager.Instance.SendRpc(packet, callback))
        //        return false;

        //    SendData(packet);
        //    return true;
        //}


        /// <summary>
        /// 发送网络包数据，实际
        /// 消息的字节流格式如下：
        /// <para>    * +------------+-------------+-------------+ -------------+</para>
        /// <para>    * |  消息长度  |  协议类型   |  协议ID     |   内容       |</para>
        /// <para>    * |    0x04    |     1       |     2       |   DCBA       |</para>
        /// <para>    * +------------+-------------+-------------+ -------------+</para>
        /// </summary>
        /// <param name="_msg">构建网络包规则</param>
        public virtual void SendData(NetMsg _msg, RpcEvent callback = null)
        {
            bool bSend = true;
            NetPacket packet = m_objNetPacketBuilder.Pack(_msg);

            if (NetOption.RPCEnable)
                bSend = RpcManager.Instance.SendRpc(packet, callback);

            if (!bSend)
                return;

            SendData(packet);
        }

        /// <summary>
        /// 发送网络包数据，实际
        /// 消息的字节流格式如下：
        /// <para>    * +------------+-------------+-------------+ -------------+</para>
        /// <para>    * |  消息长度  |  协议类型   |  协议ID     |   内容       |</para>
        /// <para>    * |    0x04    |     1       |     2       |   DCBA       |</para>
        /// <para>    * +------------+-------------+-------------+ -------------+</para>
        /// </summary>
        /// <param name="packet">发送的网络包</param>
        public virtual void SendData(NetPacket packet)
        {
            if (null == m_sSocket || !m_sSocket.Connected)
                return;

            lock (((ICollection)m_lNetPacketSend).SyncRoot)
            {
                m_lNetPacketSend.Add(packet);
            }
        }

        private void SyncSend(NetPacket packet)
        {
            if (null == m_sSocket || !m_sSocket.Connected)
                return;

            lock (m_lockObj)
            {
                try
                {
                    byte[] sendData = m_objNetPacketBuilder.Encode(packet);
                    if (m_sSocket.Send(sendData) != sendData.Length)
                    {
                        Log.WriteError("Send Data Error");
                        Disconnect();
                    }
                }
                catch (SocketException sex)
                {
                    OnHandleSocketException(sex);
                }
            }
        }

        public void SyncSendBytes(byte[] inBuffer)
        {
            if (null == m_sSocket || !m_sSocket.Connected)
                return;

            lock (m_lockObj)
            {
                try
                {
                    if (m_sSocket.Send(inBuffer) != inBuffer.Length)
                    {
                        Log.WriteError("Send Data Error");
                        Disconnect();
                    }
                }
                catch (SocketException sex)
                {
                    OnHandleSocketException(sex);
                }
            }
        }

        private void AsyncSend(NetPacket packet)
        {
            if (null == m_sSocket || !m_sSocket.Connected)
                return;

            try
            {
                byte[] sendData = m_objNetPacketBuilder.Encode(packet);
                m_sSocket.BeginSend(sendData, 0, sendData.Length, SocketFlags.None,
                    new AsyncCallback(OnSended), m_sSocket);
            }
            catch (SocketException sex)
            {
                OnHandleSocketException(sex);
            }
        }


        private void OnSended(IAsyncResult asyncSend)
        {
            try
            {
                Socket socket = (Socket)asyncSend.AsyncState;
                socket.EndSend(asyncSend);
            }
            catch (SocketException sex)
            {
                OnHandleSocketException(sex);
            }
        }


        /// <summary>
        /// 分发消息给监听者的OnHandleMessage函数
        /// </summary>
        /// <param name="packet">收到的网络包</param>
        private void DispatchMsg(NetPacket packet)
        {
            if (null == m_lListeners || 0 == m_lListeners.Count)
            {
                Log.WriteError("Socket don't have any Listener!");
                return;
            }

            m_objNetPacketBuilder.Unpack(packet);

            // 通知每一个监听者
            m_lListeners.ForEach(listener =>
            {
                listener.OnHandleMessage(this, packet);
            });

            // 如果开启了RPC
            if (NetOption.RPCEnable)
                RpcManager.Instance.Received(packet, this);
        }


        /// <summary>
        /// 发送心跳检测
        /// <para>    * +------------+-------------+-------------+</para>
        /// <para>    * |  消息长度  |  协议类型   |  协议ID     |</para>
        /// <para>    * |    0x04    |     0       |     0       |</para>
        /// <para>    * +------------+-------------+-------------+</para>
        /// </summary>
        public virtual void DoSendHeartBeat()
        {
            if(null == m_sSocket || !m_sSocket.Connected)
            {
                return;
            }

            uint curTime = (uint)System.Environment.TickCount;
            if(curTime - m_uiLastHeartBeatTime < NetOption.HeartInterval)
            {
                return;
            }

            // 发送心跳包
            

        }


        /// <summary>
        /// 发送缓存的网络包
        /// </summary>
        void DoSend()
        {
            lock (((ICollection)m_lNetPacketSend).SyncRoot)
            {
                try
                {
                    for (int i = 0; i < m_lNetPacketSend.Count; i++)
                    {
                        NetPacket packet = m_lNetPacketSend[i];
                        SyncSend(packet);
                    }
                }
                finally
                {
                    m_lNetPacketSend.Clear();
                }
            }
        }

        void DoDispatch()
        {
            lock (((ICollection)m_lNetPacketRecv).SyncRoot)
            {
                try
                {
                    for (int i = 0; i < m_lNetPacketRecv.Count; i++)
                    {
                        NetPacket packet = m_lNetPacketRecv[i];
                        DispatchMsg(packet);
                    }
                }
                finally
                {
                    m_lNetPacketRecv.Clear();
                }
            }
        }

        /// <summary>
        /// 处理Socket连接错误
        /// </summary>
        private void OnHandleSocketEvent(SocketEvent se)
        {
            if (null == m_lListeners || 0 == m_lListeners.Count)
            {
                Log.WriteError("Socket don't have any Listener!");
                return;
            }

            switch(se)
            {
                case SocketEvent.Connected:
                    {
                        // 通知每一个监听者
                        m_lListeners.ForEach(listener =>
                        {
                            listener.OnSocketConnected(this);
                        });
                        break;
                    }
                case SocketEvent.Disconnected:
                    {
                        m_lListeners.ForEach(listener =>
                        {
                            listener.OnSocketDisconnected(this);
                        });
                        break;
                    }
                case SocketEvent.TimeOut:
                    {
                        m_lListeners.ForEach(listener =>
                        {
                            listener.OnSocketConnectTimout(this);
                        });
                        break;
                    }
                case SocketEvent.Unknow:
                default:
                    {
                        break;
                    }
            }
        }

        /// <summary>
        /// 处理Socket连接错误
        /// </summary>
        /// <param name="sex"></param>
        private void OnHandleSocketException(SocketException sex)
        {
            Log.WriteException(sex);
            switch (sex.SocketErrorCode)
            {
                case SocketError.Interrupted:
                    {
                        break;
                    }
                case SocketError.TimedOut:
                    {
                        OnHandleSocketEvent(SocketEvent.TimeOut);
                        Disconnect();
                        break;
                    }
                default:
                    {
                        OnHandleSocketEvent(SocketEvent.Unknow);
                        Disconnect();
                        break;
                    }
            }
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        private void Disconnect()
        {
            Log.WriteInfo("Disconnect:" + m_strConnetHost + " port:" + m_uiConnetPort);

            ShutDown();

            m_lNetPacketSend.Clear();
            m_uiRecvBufferSize = 0;

            OnHandleSocketEvent(SocketEvent.Disconnected);

            GC.Collect();
        }

        /// <summary>
        /// 强制关闭Socket
        /// </summary>
        private void ShutDown()
        {
            m_objNetPacketBuilder.Reset();

            lock (m_lockObj)
            {
                try
                {
                    if (null != m_sSocket && m_sSocket.Connected)
                    {
                        m_sSocket.Shutdown(SocketShutdown.Both);
                        m_sSocket.Close();
                        m_sSocket = null;
                    }
                }
                catch (SocketException sex)
                {
                    OnHandleSocketException(sex);
                    throw sex;
                }
                finally
                {
                    m_sSocket = null;
                }
            }
        }

        /// <summary>
        /// 发送完数据关闭Socket
        /// </summary>
        public virtual void Close()
        {
            Log.WriteInfo("Close Socket. IP:" + m_strConnetHost + " Port:" + m_uiConnetPort);
            DoSend();
            ShutDown();
            GC.Collect();
        }
    }
}
